﻿using System;
using System.Net;
using System.Xml;
using QQ2564874169.Core.Utils;

namespace QQ2564874169.Tencent.Weixin.Request
{
    public class Unifiedorder : WXEntity
    {
        /// <summary>
        /// 微信分配的公众账号ID（企业号corpid即为此appId）
        /// </summary>
        public string appid { get; set; }

        /// <summary>
        /// 商户号
        /// </summary>
        public string mch_id { get; set; }

        /// <summary>
        /// 设备号
        /// 自定义参数，可以为终端设备号(门店号或收银设备ID)，PC网页或公众号内支付可以传"WEB"
        /// </summary>
        public string device_info { get; set; }

        /// <summary>
        /// 随机字符串
        /// </summary>
        public string nonce_str { get; set; }

        /// <summary>
        /// 签名
        /// </summary>
        public string sign { get; private set; }

        /// <summary>
        /// 签名类型
        /// </summary>
        public SignType sign_type { get; set; }

        /// <summary>
        /// 商品描述
        /// </summary>
        public string body { get; set; }

        /// <summary>
        /// 附加数据
        /// </summary>
        public string attach { get; set; }

        /// <summary>
        /// 商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*@ ，且在同一个商户号下唯一。
        /// </summary>
        public string out_trade_no { get; set; }

        /// <summary>
        /// 标价币种
        /// </summary>
        public FeeType fee_type { get; set; }

        /// <summary>
        /// 订单总金额，单位为分。
        /// </summary>
        public int total_fee { get; set; }

        /// <summary>
        /// APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP。
        /// </summary>
        public IPAddress spbill_create_ip { get; set; }

        /// <summary>
        /// 订单生成时间
        /// </summary>
        public DateTime time_start { get; set; }

        /// <summary>
        /// 订单失效时间
        /// </summary>
        public DateTime time_expire { get; set; }

        /// <summary>
        /// 异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。
        /// </summary>
        public string notify_url { get; set; }

        /// <summary>
        /// 交易类型
        /// </summary>
        public TradeType trade_type { get; set; }

        /// <summary>
        /// 商品ID，扫码支付时此参数必传。此参数为二维码中包含的商品ID，商户自行定义。
        /// </summary>
        public string product_id { get; set; }

        /// <summary>
        /// 是否限制使用信用卡。
        /// </summary>
        public bool limit_pay { get; set; }

        /// <summary>
        /// 用户标识，公众号支付时此参数必传。
        /// </summary>
        public string openid { get; set; }

        /// <summary>
        /// 场景信息
        /// </summary>
        public UnifiedorderSceneInfo scene_info { get; set; }

        protected override XmlNode GetNode(string name, object value)
        {
            if (value is DateTime)
            {
                if (name == nameof(time_start) || name == nameof(time_expire))
                {
                    value = ((DateTime) value).ToString("yyyyMMddHHmmss");
                }
            }
            if (name == nameof(limit_pay) && value is bool)
            {
                value = ((bool) value) ? "no_credit" : null;
            }
            else if (name == nameof(scene_info) && value is UnifiedorderSceneInfo)
            {
                value = JsonHelper.ToJson(value);
            }
            else if (name == nameof(spbill_create_ip))
            {
                value = ((IPAddress) value).ToString();
            }
            return base.GetNode(name, value);
        }

        public Unifiedorder()
        {
            nonce_str = Guid.NewGuid().ToString().Replace("-", "").ToLower();
            sign_type = SignType.MD5;
            fee_type = FeeType.CNY;
        }

        private void CheckValue()
        {
            if (appid == null)
                throw new ArgumentNullException(nameof(appid));
            if (mch_id == null)
                throw new ArgumentNullException(nameof(mch_id));
            if (nonce_str == null)
                throw new ArgumentNullException(nameof(nonce_str));
            if (body == null)
                throw new ArgumentNullException(nameof(body));
            if (out_trade_no == null)
                throw new ArgumentNullException(nameof(out_trade_no));
            if (total_fee <= 0)
                throw new ArgumentException(nameof(total_fee) + "必须大于0。");
            if (spbill_create_ip == null)
                throw new ArgumentNullException(nameof(spbill_create_ip));
            if (time_start < DateTime.Now.AddHours(-2))
                throw new ArgumentException(nameof(time_start) + "不能小于2小时之前。");
            if (time_expire < DateTime.Now)
                throw new ArgumentException(nameof(time_expire) + "必须大于当前时间。");
            if (notify_url == null)
                throw new ArgumentNullException(nameof(notify_url));
            if (openid == null)
                throw new ArgumentNullException(nameof(openid));

            if (time_start.AddMinutes(1) > time_expire)
                throw new ArgumentException("最短订单失效时间不能小于1分钟。");
            if (trade_type == TradeType.NATIVE && product_id == null)
                throw new ArgumentException("扫码支付时必须设置商品ID。");
            if (trade_type == TradeType.MICROPAY)
                throw new ArgumentException($"统一下单接口中交易类型不能使用：{nameof(TradeType.MICROPAY)}。");
            if (notify_url.Contains("?"))
                throw new ArgumentException("通知地址不能带参数。");
        }

        private void SetSign(string app_secret)
        {
            var dict = base.ToXml().ToDictionary(node => node.Name != nameof(sign));
            sign = WxSignHelper.PaySign(dict, app_secret, sign_type);
        }

        public XmlDocument ToXml(string app_secret)
        {
            CheckValue();
            SetSign(app_secret);
            return base.ToXml();
        }
    }
}
